home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Sample Code / AppsToGo / Kibitz / DoEvent.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-09-22  |  18.0 KB  |  722 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** File:        doevent.c
  5. ** Written by:  Eric Soldan
  6. **
  7. ** Copyright © 1990-1992 Apple Computer, Inc.
  8. ** All rights reserved. */
  9.  
  10.  
  11.  
  12. /*****************************************************************************/
  13.  
  14.  
  15.  
  16. #include "Kibitz.h"                /* Get the Kibitz includes/typedefs, etc.    */
  17. #include "KibitzCommon.h"        /* Get the stuff in common with rez.        */
  18. #include "Kibitz.protos"        /* Get the prototypes for Kibitz.            */
  19.  
  20. #ifndef __AEUTILS__
  21. #include <AEUtils.h>
  22. #endif
  23.  
  24. #ifndef __DESK__
  25. #include <Desk.h>
  26. #endif
  27.  
  28. #ifndef __DISKINIT__
  29. #include <DiskInit.h>
  30. #endif
  31.  
  32. #ifndef __ERRORS__
  33. #include <Errors.h>
  34. #endif
  35.  
  36. #ifndef __MENUS__
  37. #include <Menus.h>
  38. #endif
  39.  
  40. #ifndef __OSEVENTS__
  41. #include <OSEvents.h>
  42. #endif
  43.  
  44. #ifndef __RESOURCES__
  45. #include <Resources.h>
  46. #endif
  47.  
  48. #ifndef __TEXTEDITCONTROL__
  49. #include <TextEditControl.h>
  50. #endif
  51.  
  52. #ifndef __TOOLUTILS__
  53. #include <ToolUtils.h>
  54. #endif
  55.  
  56. #ifndef __UTILITIES__
  57. #include <Utilities.h>
  58. #endif
  59.  
  60.  
  61.  
  62. /*****************************************************************************/
  63.  
  64.  
  65.  
  66. extern Cursor    *gCurrentCursor;
  67.  
  68. unsigned long    gStatusTime;
  69. Boolean            gAlertTimeout, gComputerResigns;
  70.  
  71.  
  72.  
  73. /*****************************************************************************/
  74. /*****************************************************************************/
  75.  
  76. #ifdef applec
  77. #pragma segment DoEvent
  78. #endif
  79.  
  80. /*****************************************************************************/
  81. /*****************************************************************************/
  82.  
  83.  
  84.  
  85. /* Do the right thing for an event.  Determine what kind of event it is, and
  86. ** call the appropriate routines. */
  87.  
  88. void    DoEvent(EventRecord *event)
  89. {
  90.     short            part, err;
  91.     WindowPtr        window;
  92.     char            key;
  93.     Point            aPoint;
  94.     FileRecHndl        frHndl;
  95.     ControlHandle    ctl;
  96.     long            tick;
  97.     Rect            boardRct;
  98.     short            retval, width, dir;
  99.  
  100.     switch(event->what) {
  101.  
  102.         case nullEvent:
  103.             DoIdleTasks(true);
  104.             break;
  105.  
  106.         case mouseDown:
  107.             gCurrentCursor = nil;
  108.                 /* No shortcuts when we recalculate the cursor region. */
  109.  
  110.             part = FindWindow(event->where, &window);
  111.             if (part != inContent) DoSetCursor(&qd.arrow);
  112.  
  113.             switch(part) {
  114.  
  115.                 case inContent:
  116.                     if (window != FrontWindow()) {
  117.                         SelectWindow(window);
  118.                         if (IsAppWindow(window)) {
  119.                             DoUpdate(window);
  120.                             boardRct = GlobalBoardRect(window);
  121.                             if (PtInRect(event->where, &boardRct))
  122.                                 DoEvent(event);
  123.                         }
  124.                                     /* Do first click if over board. */
  125.                     } else
  126.                         DoContentClick(window, event);
  127.                     break;
  128.  
  129.                 case inDrag:            
  130.                     DragWindow(window, event->where, &qd.screenBits.bounds);
  131.                     break;        /* Pass screenBits.bounds to
  132.                                 ** get all gDevices. */
  133.  
  134.                 case inGoAway:
  135.                     if (TrackGoAway(window, event->where)) {
  136.                         CloseOneWindow(window, iClose);
  137.                     }
  138.                     break;
  139.  
  140.                 case inGrow:
  141.                     break;
  142.  
  143.                 case inMenuBar:        /* Process mouse menu command (if any). */
  144.                     AdjustMenus();
  145.                     DoMenuCommand(MenuSelect(event->where), event);
  146.                     break;
  147.  
  148.                 case inSysWindow:    /* Let the system handle the mouseDown. */
  149.                     SystemClick(event, window);
  150.                     break;
  151.  
  152.                 case inZoomIn:
  153.                 case inZoomOut:
  154.                     if (TrackBox(window, event->where, part)) {
  155.                         width = window->portRect.right - window->portRect.left;
  156.                         if (width == rWindowWidth) width = rJustBoardWindowWidth;
  157.                         else                       width = rWindowWidth;
  158.                         ZoomToWindowDevice(window, width, rWindowHeight, inZoomOut, true);
  159.                     }
  160.                     break;
  161.  
  162.             }
  163.             break;
  164.  
  165.         case activateEvt:
  166.             gCurrentCursor = nil;        /* No shortcuts when we recalculate the cursor region. */
  167.             DoActivate((WindowPtr)event->message, (event->modifiers & activeFlag));
  168.             break;
  169.  
  170.         case autoKey:
  171.         case keyDown:                    /* Check for menukey equivalents. */
  172.             key = event->message & charCodeMask;
  173.             if (event->modifiers & cmdKey) {        /* Command key down. */
  174.                 if (event->what == keyDown) {
  175.                     AdjustMenus();
  176.                         /* Enable/disable/check menu items properly. */
  177.                     DoMenuCommand(MenuKey(key), event);
  178.                 }
  179.             }
  180.             else {
  181.                 if (!IsAppWindow(window = FrontWindow())) break;
  182.                 frHndl = (FileRecHndl)GetWRefCon(window);
  183.                 if (key == 0x03) {
  184.                     ctl = (*frHndl)->doc.sendMessage;
  185.                     if (!(*ctl)->contrlHilite) {
  186.                         HiliteControl(ctl, 1);
  187.                         tick = TickCount();
  188.                         while (TickCount() < tick + 10) {};
  189.                         HiliteControl(ctl, 0);
  190.                         SendMssg(frHndl, kTextMssg);
  191.                     }
  192.                 }
  193.                 else {
  194.                     if (event->modifiers & optionKey) {
  195.                         dir = 0;
  196.                         if ((key == chLeft)  || (key == chUp))   dir = -1;
  197.                         if ((key == chRight) || (key == chDown)) dir = 1;
  198.                         if (dir) {
  199.                             SetFilePort(frHndl);
  200.                             RepositionBoard(frHndl, (*frHndl)->doc.gameIndex + dir, true);
  201.                             ImageDocument(frHndl, true);
  202.                             AdjustGameSlider(frHndl);
  203.                             if ((*frHndl)->doc.twoPlayer) SendGame(frHndl, kResync, nil);
  204.                             return;
  205.                         }
  206.                     }
  207.                     retval = CTEKey(window, event);
  208.                     if (retval) {
  209.                         if (retval == 2) (*frHndl)->fileState.docDirty = true;
  210.                             /* Out-box was edited, so dirty document. */
  211.                         AdjustMenus();        /* Avoid unnecessary DoCursor() and speed */
  212.                         return;                /* up TextEdit entry. */
  213.                     }
  214.                 }
  215.             }
  216.             break;
  217.  
  218.         case diskEvt:
  219.             gCurrentCursor = nil;
  220.                 /* No shortcuts when we recalculate the cursor region. */
  221.  
  222.             if (HiWord(event->message) != noErr) {
  223.                 SetPt(&aPoint, kDILeft, kDITop);
  224.                 err = DIBadMount(aPoint, event->message);
  225.             }
  226.             break;        /* It is not a bad idea to at least call DIBadMount
  227.                         ** in response to a diskEvt, so that the user can
  228.                         ** format a floppy.
  229.                         */
  230.         case kHighLevelEvent:
  231.             gCurrentCursor = nil;
  232.                 /* No shortcuts when we recalculate the cursor region. */
  233.  
  234.             DoHighLevelEvent(event);
  235.             break;
  236.  
  237.         case kOSEvent:
  238.             gCurrentCursor = nil;
  239.                 /* No shortcuts when we recalculate the cursor region. */
  240.  
  241.             switch ((event->message >> 24) & 0x0FF) {
  242.                     /* Must logical and with 0x0FF to get only low byte. */
  243.                     /* High byte of message. */
  244.  
  245.                 case kMouseMovedMessage:
  246.                     DoIdleTasks(true);
  247.                     break;
  248.  
  249.                 case kSuspendResumeMessage:
  250.                         /* Suspend/resume is also an activate/deactivate. */
  251.                     gInBackground = !((event->message) & kResumeMask);
  252.                     CTEConvertClipboard((event->message) & convertClipboardFlag, !gInBackground);
  253.                     DoActivate(FrontWindow(), !gInBackground);
  254.                     break;
  255.             }
  256.             break;
  257.  
  258.         case updateEvt:
  259.             DoUpdate((WindowPtr)event->message);
  260.             break;
  261.  
  262.     }
  263.  
  264.     DoCursor();
  265.     AdjustMenus();
  266. }
  267.  
  268.  
  269.  
  270. /*****************************************************************************/
  271.  
  272.  
  273.  
  274. /* This is called when a window is activated or deactivated. */
  275.  
  276. void    DoActivate(WindowPtr window, Boolean becomingActive)
  277. {
  278.     FileRecHndl        frHndl;
  279.     short            hilite;
  280.     ControlHandle    ctl;
  281.  
  282.     NotifyCancel();
  283.  
  284.     if (IsAppWindow(window)) {
  285.  
  286.         frHndl = (FileRecHndl)GetWRefCon(window);
  287.  
  288.         SetPort(window);
  289.         SetOrigin((*frHndl)->doc.arrangeBoard * 4096, 0);
  290.  
  291.         hilite = 0;
  292.         if (!becomingActive) hilite = 255;
  293.  
  294.         HiliteControl((*frHndl)->doc.gameSlider, hilite);
  295.         HiliteControl((*frHndl)->doc.resign, hilite);
  296.         HiliteControl((*frHndl)->doc.draw, hilite);
  297.  
  298.         if (!(*frHndl)->doc.twoPlayer) hilite = 255;
  299.  
  300.         HiliteControl(ctl = (*frHndl)->doc.sendMessage, hilite);
  301.         OutlineControl(ctl);
  302.         HiliteControl((*frHndl)->doc.beepOnMove, hilite);
  303.         HiliteControl((*frHndl)->doc.beepOnMssg, hilite);
  304.  
  305.         if (!SoundInputAvaliable()) hilite = 255;
  306.         HiliteControl((*frHndl)->doc.record, hilite);
  307.         if (!(*frHndl)->doc.sound) hilite = 255;
  308.         HiliteControl((*frHndl)->doc.sendSnd, hilite);
  309.  
  310.         if (!(*frHndl)->doc.arrangeBoard) CTEWindActivate(window, true);
  311.  
  312.         DoDrawControls(window, true);
  313.         SetOrigin(0, 0);
  314.     }
  315. }
  316.  
  317.  
  318.  
  319. /*****************************************************************************/
  320.  
  321.  
  322.  
  323. /* This is called when an update event is received for a window.  It calls
  324. ** ImageDocument to draw the contents of an application window.  As an
  325. ** effeciency measure that does not have to be followed, it calls the drawing
  326. ** routine only if the visRgn is non-empty.  This will handle situations where
  327. ** calculations for drawing or drawing itself is very time-consuming. */
  328.  
  329. void    DoUpdate(WindowPtr window)
  330. {
  331.     if (IsAppWindow(window)) {
  332.         BeginUpdate(window);                /* This sets up the visRgn. */
  333.         if (!EmptyRgn(window->visRgn)) {    /* Draw if updating needs doing. */
  334.             SetPort(window);
  335.             ImageDocument((FileRecHndl)GetWRefCon(window), false);
  336.         }
  337.         EndUpdate(window);
  338.     }
  339. }
  340.  
  341.  
  342.  
  343. /*****************************************************************************/
  344.  
  345.  
  346.  
  347. /* This is called when a mouse-down event occurs in the content of a window.
  348. ** Other applications might want to call FindControl, TEClick, etc., to
  349. ** further process the click. */
  350.  
  351. void    DoContentClick(WindowPtr window, EventRecord *event)
  352. {
  353.     Boolean            invertBoard, legalMove;
  354.     short            fromRow, fromCol, toRow, toCol, i, myColor;
  355.     short            fromSq, toSq, promotion, piece, item;
  356.     short            numLegalMoves, part, twoPlayer, whosMove;
  357.     OSErr            err;
  358.     long            ref;
  359.     Point            clickLoc, releaseLoc;
  360.     Rect            boardRect, fromRect;
  361.     FileRecHndl        frHndl;
  362.     MoveListHndl    legalMoves;
  363.     DialogPtr        promoteDialog;
  364.     short            itemType, ctlNum, val, action;
  365.     Handle            itemHndl;
  366.     Rect            itemRect;
  367.     ControlHandle    ctlHit;
  368.     EventRecord        option;
  369.  
  370.     if (!IsAppWindow(window)) return;
  371.  
  372.     frHndl = (FileRecHndl)GetWRefCon(window);
  373.     SetPort(window);
  374.     SetOrigin((*frHndl)->doc.arrangeBoard * 4096, 0);
  375.  
  376.     twoPlayer = (*frHndl)->doc.twoPlayer;
  377.  
  378.     clickLoc = event->where;
  379.     GlobalToLocal(&clickLoc);
  380.  
  381.     if (CTEClick(window, event, &action)) {
  382.         SetOrigin(0, 0);
  383.         return;
  384.     }
  385.         /* If TextEdit control handled the click, we are done. */
  386.  
  387.     part = FindControl(clickLoc, window, &ctlHit);
  388.     if (part) {
  389.         ctlNum = ref = GetControlReference(ctlHit);
  390.         if ((ref) && (ref < 10)) {
  391.             if (TrackControl(ctlHit, clickLoc, nil)) {
  392.                 switch (ctlNum) {
  393.                     case 1:
  394.                         SendMssg(frHndl, kTextMssg);
  395.                         break;
  396.                     case 2:
  397.                     case 3:
  398.                         SetControlValue(ctlHit, val = (GetControlValue(ctlHit) ^ 1));
  399.                         if (val)
  400.                             if ((event->modifiers) & optionKey) SendMssg(frHndl, kBeepMssg);
  401.                         break;
  402.                     case 4:
  403.                     case 5:
  404.                         SetControlValue(ctlHit, 1);
  405.                         SetControlValue((*frHndl)->doc.wbStart[5 - ctlNum], 0);
  406.                         (*frHndl)->doc.startColor = ctlNum - 4;
  407.                         break;
  408.                     case 6:
  409.                         EndTheGame(frHndl, kWhiteResigns + (*frHndl)->doc.myColor);
  410.                         SayTheMove(frHndl);
  411.                         UpdateGameStatus(frHndl);
  412.                         if (twoPlayer) {
  413.                             SendGame(frHndl, kIsMove, nil);
  414.                                 /* Show the dialog at the other end. */
  415.                             SendGame(frHndl, kResync, nil);
  416.                                 /* Make sure that simultaneous hits on the
  417.                                 ** resign button are taken care of. */
  418.                         }
  419.                         break;
  420.                     case 7:
  421.                         i = ((*frHndl)->doc.drawBtnState ^ 0x02);
  422.                         if (!twoPlayer) i = 0x06;
  423.                         if (i >= 0x06) {
  424.                             EndTheGame(frHndl, kDrawGame);
  425.                             SayTheMove(frHndl);
  426.                         }
  427.                         DrawButtonTitle(frHndl, i);
  428.                         if (twoPlayer) {
  429.                             SendGame(frHndl, kIsMove, nil);
  430.                                 /* Show the dialog at the other end. */
  431.                             SendGame(frHndl, kResync, nil);
  432.                                 /* Make sure that simultaneous hits on the
  433.                                 ** draw button are taken care of. */
  434.                         }
  435.                         break;
  436.                     case 8:
  437.                         err = RecordSound(frHndl);
  438.                         if ((err) && (err != userCanceledErr))
  439.                             Alert(rErrorAlert, gAlertFilterUPP);
  440.                         break;
  441.                     case 9:
  442.                         SendMssg(frHndl, kSoundMssg);
  443.                         break;
  444.                 }
  445.             }
  446.         }
  447.         SetOrigin(0, 0);
  448.  
  449.         if (ctlHit == (*frHndl)->doc.gameSlider)
  450.             TrackControl(ctlHit, clickLoc, nil);
  451.  
  452.         return;
  453.     }
  454.  
  455.     if ((*frHndl)->fileState.readOnly) {
  456.         SetOrigin(0, 0);
  457.         return;
  458.     }                    /* Don't allow changes if read-only. */
  459.  
  460.     if ((*frHndl)->doc.arrangeBoard) {
  461.         DoArrangeBoard(frHndl, event, clickLoc);
  462.         return;
  463.     }
  464.  
  465.     SetOrigin(0, 0);
  466.  
  467.     invertBoard = (*frHndl)->doc.invertBoard;
  468.  
  469.     boardRect = BoardRect();
  470.     if (!PtInRect(clickLoc, &boardRect)) return;
  471.  
  472.     fromRow = (clickLoc.v - kBoardVOffset) / kBoardSqSize;
  473.     fromCol = (clickLoc.h - kBoardHOffset) / kBoardSqSize;
  474.     fromRect.top    = 1 + fromRow * kBoardSqSize;
  475.     fromRect.left   = 1 + fromCol * kBoardSqSize;
  476.     fromRect.bottom = fromRect.top  + 32;
  477.     fromRect.right  = fromRect.left + 32;
  478.  
  479.     if (invertBoard) {
  480.         fromRow = 7 - fromRow;
  481.         fromCol = 7 - fromCol;
  482.     }
  483.     fromSq  = START_IBNDS + 10 * fromRow + fromCol;
  484.  
  485.     if (GameStatus(frHndl) != kGameContinues) return;
  486.         /* Game over, so no moves. */
  487.  
  488.     if ((*frHndl)->doc.resync != kIsMove) return;
  489.         /* Don't allow moves until we are resynced. */
  490.  
  491.     numLegalMoves = (*frHndl)->doc.numLegalMoves;
  492.     legalMoves    = (*frHndl)->doc.legalMoves;
  493.  
  494.     for (i = 0; i < numLegalMoves; ++i)
  495.         if ((**legalMoves)[i].moveFrom == fromSq) break;
  496.  
  497.     if (i == numLegalMoves) return;
  498.         /* Clicked on a empty square or on a piece that can't move. */
  499.  
  500.     whosMove = WhosMove(frHndl);
  501.     myColor  = (*frHndl)->doc.myColor;
  502.     OSEventAvail(nullEvent, &option);
  503.     if (option.modifiers & optionKey)
  504.         if (myColor != kMessageDoc) myColor = whosMove;
  505.  
  506.     if ((twoPlayer) && (whosMove != myColor)) return;
  507.         /* It's the other player's turn. */
  508.  
  509.     if ((whosMove == WHITE) && ((*frHndl)->doc.compMovesWhite)) return;
  510.     if ((whosMove == BLACK) && ((*frHndl)->doc.compMovesBlack)) return;
  511.         /* Computer is moving this color, so ignore click. */
  512.  
  513.     SetCursor(*GetCursor(closedHandCursor));
  514.     gCurrentCursor = nil;
  515.  
  516.     releaseLoc.h = releaseLoc.v = 0x4000;
  517.     MoveThePiece(frHndl, fromSq, fromRect, clickLoc, &releaseLoc);
  518.  
  519.     toRow = (releaseLoc.v - kBoardVOffset) / kBoardSqSize;
  520.     toCol = (releaseLoc.h - kBoardHOffset) / kBoardSqSize;
  521.     if (invertBoard) {
  522.         toRow = 7 - toRow;
  523.         toCol = 7 - toCol;
  524.     }
  525.     toSq = START_IBNDS + 10 * toRow + toCol;
  526.  
  527.     for (i = 0; i < numLegalMoves; ++i)
  528.         if (
  529.             ((**legalMoves)[i].moveFrom == fromSq) &&
  530.             ((**legalMoves)[i].moveTo   == toSq)
  531.         ) break;
  532.  
  533.     promotion = QUEEN;        /* If there is a promotion, assume queen. */
  534.  
  535.     legalMove = (i < numLegalMoves);
  536.     if (legalMove) {
  537.  
  538.         if ((toRow == 0) || (toRow == 7)) {        /* Possible pawn promotion... */
  539.  
  540.             piece = (*frHndl)->doc.theBoard[fromSq];
  541.             if (piece < 0) piece = -piece;
  542.  
  543.             if (piece == PAWN) {                /* It is a pawn promotion... */
  544.  
  545.                 if (option.modifiers & shiftKey) promotion = QUEEN;
  546.                 else {
  547.  
  548.                     MakeMove(frHndl, fromSq, toSq, PAWN);
  549.                     ImageDocument(frHndl, true);
  550.                     MakeMove(frHndl, -1, 0, 0);
  551.  
  552.                     promoteDialog = GetCenteredDialog(rPawnPromotion, nil,
  553.                                                       FrontWindow(), (WindowPtr)-1L);
  554.                     if (promoteDialog) {
  555.                         SetPort(promoteDialog);
  556.                         OutlineDialogItem(promoteDialog, 1);
  557.                         DoSetCursor(&qd.arrow);
  558.  
  559.                         for (item = QUEEN;;) {
  560.                             if (promotion != item) {
  561.                                 GetDialogItem(promoteDialog, promotion + 1, &itemType, &itemHndl, &itemRect);
  562.                                 SetControlValue((ControlHandle)itemHndl, false);
  563.                             }
  564.  
  565.                             GetDialogItem(promoteDialog, item + 1, &itemType, &itemHndl, &itemRect);
  566.                             SetControlValue((ControlHandle)itemHndl, true);
  567.                             promotion = item;
  568.  
  569.                             ModalDialog(gKeyEquivFilterUPP, &item);
  570.                             if (item == 1) break;
  571.                             --item;
  572.                         }
  573.                         DisposeDialog(promoteDialog);
  574.                         SetPort(window);
  575.                     }
  576.                 }
  577.             }
  578.         }
  579.     }
  580.  
  581.     if (GameStatus(frHndl)) ImageDocument(frHndl, true);
  582.     else {
  583.         if (legalMove) MakeMove(frHndl, fromSq, toSq, promotion);
  584.         ImageDocument(frHndl, true);
  585.         DrawTime(frHndl);
  586.         if (legalMove) AdjustGameSlider(frHndl);
  587.         DrawButtonTitle(frHndl, twoPlayer);
  588.         UpdateGameStatus(frHndl);
  589.         if ((legalMove) && (twoPlayer)) SendGame(frHndl, kIsMove, nil);
  590.         if (legalMove) {
  591.             SayTheMove(frHndl);
  592.             AlertIfGameOver(frHndl);
  593.         }
  594.         (frHndl);
  595.     }
  596. }
  597.  
  598.  
  599.  
  600. /*****************************************************************************/
  601.  
  602.  
  603.  
  604. extern TheDoc    newDocData;
  605.  
  606. short    AlertIfGameOver(FileRecHndl frHndl)
  607. {
  608.     WindowPtr        oldPort;
  609.     DialogPtr        dialogPtr;
  610.     short            status, i, theAlert, item;
  611.     Str255            gameStatMssg;
  612.     Handle            rsrc, txt;
  613.     long            l;
  614.     static Str255    whichMessage;
  615.     static ModalFilterUPP    statusFilterUPP;
  616.  
  617.     theAlert = rGameStat;
  618.  
  619.     if ((status = GameStatus(frHndl)) != kGameContinues) {
  620.  
  621.         if (gComputerResigns) {
  622.             if (!whichMessage[0]) {
  623.                 rsrc = GetResource('STR#', rComputerResigns);
  624.                 whichMessage[0] = **(short **)rsrc;
  625.                 for (i = 1; i <= whichMessage[0]; ++i) whichMessage[i] = i;
  626.             }
  627.             theAlert = rComputerResigns;
  628.             gComputerResigns = false;
  629.             l  = Random();
  630.             l &= 0x0000FFFFL;
  631.             l *= whichMessage[0];
  632.             l /= 0x10000L;
  633.             status = whichMessage[++l];
  634.             whichMessage[l] = whichMessage[whichMessage[0]--];
  635.         }
  636.  
  637.         UpdateGameStatus(frHndl);
  638.         GetIndString(gameStatMssg, theAlert, status);
  639.  
  640.         ParamText(gameStatMssg, nil, nil, nil);
  641.         DoSetCursor(&qd.arrow);
  642.  
  643.         gStatusTime = TickCount();
  644.         if (((*frHndl)->doc.compMovesWhite) && ((*frHndl)->doc.compMovesBlack))
  645.             status = kGameContinues;
  646.  
  647.         gAlertTimeout = false;
  648.  
  649.         GetPort(&oldPort);
  650.         dialogPtr = GetCenteredDialog(theAlert, nil, nil, (WindowPtr)-1);
  651.         if (dialogPtr) {
  652.             SetPort(dialogPtr);
  653.             DrawDialog(dialogPtr);
  654.             OutlineDialogItem(dialogPtr, 1);
  655.             SetEmptyRgn(((WindowPeek)dialogPtr)->updateRgn);    /* Did it by hand -- prevent redraw. */
  656.  
  657.             if ((*frHndl)->doc.doSpeech) {
  658.                 txt = NewHandle(gameStatMssg[0]);
  659.                 if (txt) {
  660.                     for (i = 1; i <= gameStatMssg[0]; ++i)
  661.                         if (gameStatMssg[i] == (unsigned char)'’')
  662.                             gameStatMssg[i] = '\'';
  663.                     BlockMove(gameStatMssg + 1, *txt, gameStatMssg[0]);
  664.                     SayText(nil, txt, (*frHndl)->doc.theVoice);
  665.                     DisposeHandle(txt);
  666.                 }
  667.             }
  668.  
  669.             if (!statusFilterUPP)
  670.                 statusFilterUPP = NewModalFilterProc(statusFilter);
  671.             for (;;) {
  672.                 ModalDialog(statusFilterUPP, &item);
  673.                 if (item == 1) break;
  674.             }
  675.             DisposeDialog(dialogPtr);
  676.         }
  677.         else gAlertTimeout = true;
  678.         SetPort(oldPort);
  679.  
  680.         if (!gAlertTimeout) status = GameStatus(frHndl);
  681.  
  682.         if (status == kGameContinues) {
  683.             oldPort = SetFilePort(frHndl);
  684.             RepositionBoard(frHndl, 0, true);
  685.             UpdateGameStatus(frHndl);
  686.             if ((*frHndl)->doc.twoPlayer) SendGame(frHndl, kResync, nil);
  687.             AdjustGameSlider(frHndl);
  688.             for (i = 0; i < 2; ++i)
  689.                 if ((*frHndl)->doc.timeLeft[i] != -1)
  690.                     (*frHndl)->doc.timeLeft[i] = (*frHndl)->doc.displayTime[i] =
  691.                         (*frHndl)->doc.defaultTime[i];
  692.             (*frHndl)->doc.timerRefTick = TickCount();
  693.             UpdateTime(frHndl, false);
  694.             DrawTime(frHndl);
  695.             SetPort(oldPort);
  696.         }            
  697.     }
  698.  
  699.     return(status);
  700. }
  701.  
  702.  
  703.  
  704. /*****************************************************************************/
  705.  
  706.  
  707.  
  708. pascal Boolean    statusFilter(DialogPtr dlg, EventRecord *event, short *item)
  709. {
  710.     if (KeyEquivFilter(dlg, event, item)) return(true);
  711.     if ((gStatusTime) && (gStatusTime + 600 < TickCount())) {
  712.         *item = 1;
  713.         gAlertTimeout = true;
  714.         return(true);
  715.     }
  716.  
  717.     return(false);
  718. }
  719.  
  720.  
  721.  
  722.